import socket, sys, time
import re, time, struct, exceptions
from collections import deque


protocols = {socket.IPPROTO_UDP:'udp',
             socket.IPPROTO_TCP:'tcp',}


class trafficParser():

    def __init__(self, fileName, interface, interval=None):
        self.fileName = fileName
        self.interface = interface
        self.session = []
        self.sLine = 0
        self.eLine = 0
        self.packetArray = deque()
        self.timeArray = deque()

        if interval:
            self.interval = float(interval)/1000
        else:
            self.interval = None


    '''
    Tries to determine the format which has been used.
    If no other formats are detected, default format is assumed.
    '''
    def findFormat(self, fl):
        
        bg = fl.read(8)

        try:
            if "\xa1\xb2\xc3\xd4" in bg:
                if "\x00\x02\x00\04" in bg:
                    return {'format':'pcap','reversed':False}
                else:
                    raise pcapError("Pcap-version not supported.")
            elif "\xd4\xc3\xb2\xa1" in bg:
                if "\x02\x00\x04\x00" in bg:
                    return {'format':'pcap','reversed':True}
                else:
                    raise pcapError("Pcap-version not supported.")
            else: return {'format':'default'}
            
        except IndexError:
            pass
        except pcapError as e:
            print e
            return -1
        

    def replaySession(self):

        try:
            fl = open(self.fileName, 'r')
        except IOError:
            print "File can't be processed."
            return -1

        ff = self.findFormat(fl)
        if ff==-1:
            return -1

        fl.seek(0)

        if ff['format']=='pcap':
            
            if self.pcapParse(fl, ff['reversed'])<0:
                #print "It was negative"
                return -1
        
        elif ff['format']=='default':

            if self.sessionParse(fl)<0:
                return -1

            fl.seek(0)
            
            if self.fileParse(fl)<0:
                return -1
            
        fl.close()
        
        i = 0
        prc = -1
        length = len(self.packetArray)
        #Try to initialize interface
        try:
            sock = socket.socket(socket.AF_PACKET,socket.SOCK_RAW)
            sock.bind((self.interface,0))
        except:
            print ("Packages could not be send.")
            return -1

        #Replicate event
        print "\nResending all the packages..."
        
        try:
            while True:
                sock.send(self.packetArray.popleft())
                i+=1
                if len(self.packetArray)==0:
                    break

                #Just some visualization
                if ((i*100)/length)!=prc:
                    prc = (i*100)/length
                    print prc, "%"

                if not self.interval:
                    wait = self.timeArray.popleft()
                    time.sleep(wait)
                else:
                    time.sleep(self.interval)

        except KeyboardInterrupt:
            print "Interrupted. Terminating.."
            
        print "Packages send: ",i

        return 1
    

    def fileParse(self, fl, separator=','):

        #Seek the end point and reset the pointer
        for x in range(0, self.eLine):
            fl.readline()

        endPoint = fl.tell()
        fl.seek(0)

        #Skip to the beginning of the wanted session
        for x in range(0, self.sLine):
            fl.readline()

        data = fl.read(endPoint)

        tempArray = []
        packet = ""
        temp = ""
        pointer = 0
        dot = 0

        print "Started parsing packets from '"+self.fileName+"'."
        current = time.time()

        #Read package information until the end arrives
        while True:

            pointer = data.find('[',pointer)
            if pointer==-1:
                break
            else: pointer += 1
            while data[pointer]!=']':
                if data[pointer]!=separator:
                    packet += data[pointer]
                pointer += 1

            for i in range(0, len(packet), 2):
                try:
                    temp += chr(int(packet[i:i+2],16))
                except ValueError:
                    print "File contains erronous data."
                    return -1

            self.packetArray.append(temp)
                 
            packet = ""
            temp = ""
            
            dot += 1
            if (dot%200==0):
                sys.stdout.write('.')
                sys.stdout.flush()
            
            
        rules = ' [0-9]+:[0-9]+:[0-9]+,[0-9]+ '
        #Reset file pointer before parsing timestamps
        fl.seek(0)
        for x in range(0, self.sLine):
            fl.readline()

        #Read timestamps until the end
        while fl.tell()<=endPoint:
            line = fl.readline()
            if line=="": break
            g = re.search(rules, line)
            if g:
                aika=(float(g.group(0)[1:3])*3600+
                      float(g.group(0)[4:6])*60+
                      float(g.group(0)[7:13].replace(',','.')))
                tempArray.append(aika)

        i = 0
        while i<(len(tempArray)-1):
            self.timeArray.append(tempArray[i+1]-tempArray[i])
            i+=1

        tempArray = []
            
        if not self.packetArray:
            print "No packets detected."
            return -1

        print ("\nParsing complete!\nNumber of packets "+
                "found: %d" % len(self.packetArray))
        print "Parsing took: ",time.time()-current," seconds."

        return 1


    '''
    Reads every session start and end time from a file
    and asks the user which session should be replicated
    '''
    def sessionParse(self, fl):

        rules = '[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+,[0-9]+ '
        start = 0
        end = 0

        while True:
            line = fl.readline()
            if line=="": break
            if line.find('-- Session started --')!=-1:
                start+=1
                g = re.search(rules, line)
                self.session.append(start)
                self.session.append(self.sLine)
                self.session.append(g.group(0))
                print "Session ",start," started at: ",g.group(0)," at line: ",self.sLine

            if line.find('-- Session ended --')!=-1:
                end+=1
                g = re.search(rules, line)
                self.session.append(end)
                self.session.append(self.eLine)
                self.session.append(g.group(0))
                print "Session ",end," ended at: ",g.group(0)," at line: ",self.eLine

            self.sLine+=1
            self.eLine+=1

        if start!=end:
            print ("Warning! There's errors with session"+
                   " start or end declarations.")

        if len(self.session)==0:
            print "File doesn't contain sessions."
            return -1

        sel = raw_input("Which session would you like to replay? ")

        while True:
            try:
                int(sel)
                if int(sel)<=0 or int(sel)>(len(self.session)/5):
                    raise ValueError
                break
            except ValueError: sel=raw_input("Give a correct session number: ")

        
        for x in range(0, len(self.session), 6):
            if(self.session[x]==int(sel)):
                self.sLine = self.session[x+1]
                self.eLine = self.session[x+4]
        
        return 1


    #Parses pcap-files and tries to replicate traffic in them
    def pcapParse(self, fl, reverse):
        
        fl.seek(24) #Skips the global header
        header = ""
        data = ""
        tempTime = 0.0
        temp = 0.0
        tempSec = ""
        tempMicro = ""
        pLen = 0
        packets = 0

        print "Started parsing packets from a file '{}'.".format(self.fileName)

        while 1:
            try:
                #Parse packet header
                header = fl.read(16)
                
                if reverse:
                    header = (header[0:4][::-1]+
                              header[4:8][::-1]+
                              header[8:12][::-1]+
                              header[12:16][::-1])
                
                if len(header)!=16: break

                packets += 1

                tempSec = str(struct.unpack('!I',header[0:4])[0])
                tempMicro = str(struct.unpack('!I',header[4:8])[0]).zfill(6)
                #Micro seconds can not be bigger than 1000000, i.e. 0x000f4240

                tempTime = float('.'.join([tempSec,tempMicro]))
                #tempTime=float('.'.join([str(x) for x in struct.unpack('!II',header[:8])]))
                #tempTime=float('.'.join(map(str,struct.unpack('!ii',header[:8]))))

                if (tempTime-temp)<0:
                    raise pcapError("Incorrect time values.")
                    pass
                
                if temp!=0:
                    self.timeArray.append(tempTime-temp)
                
                temp=tempTime
                
                pLen = struct.unpack('>I',header[8:12])[0]
                
                #Parse packet content
                data = fl.read(pLen)
                if len(data)!=pLen: raise pcapError("Packet length mismatch.")

                self.packetArray.append(data)

            except pcapError as e:
                print e.msg
                return -1

        print "Parsing complete. Total amount of packets found: {}".format(packets)
        return 0


class pcapError(Exception):
    def __init__(self, msg):
        self.msg = "Pcap error: {}".format(msg)
